package com.gdut.huayan.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
//import com.gdut.huayan.constant.WechatConstant;
import com.gdut.huayan.constant.WechatConstant;
import com.gdut.huayan.dao.*;
import com.gdut.huayan.entity.dto.PageQuery;
import com.gdut.huayan.entity.po.*;
import com.gdut.huayan.entity.vo.*;
import com.gdut.huayan.service.OrderService;
import com.gdut.huayan.service.WechatService;
import com.gdut.huayan.service.exception.BaseException;
import com.gdut.huayan.utils.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private CharityMapper charityMapper;

    @Autowired
    private NameMapper nameMapper;

    @Autowired
    private TabletMapper tabletMapper;

    @Autowired
    private WechatService wechatService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RefundMapper refundMapper;

    @Autowired
    private RefundDetailMapper refundDetailMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    @Transactional
    public void callback(HttpServletRequest request) {
        try {
            Map<String, String> map = wechatService.parseCallback(request);
            log.warn(map.toString());
            if ("SUCCESS".equals(map.get("status"))) {
                QueryWrapper<Order> wrapper = new QueryWrapper<>();
                wrapper.eq("order_number",map.get("orderNumber"));
                Order order = baseMapper.selectOne(wrapper);
                Order updateOrder = new Order();
                updateOrder.setId(order.getId());
                updateOrder.setSuccessTime(LocalDateTime.now());
                updateOrder.setStatus(2);
                baseMapper.updateById(updateOrder);
                //new LambdaUpdateChainWrapper<>(baseMapper).eq(Order::getOrderNumber, map.get("orderNumber")).set(Order::getSuccessTime, LocalDateTime.now()).set(Order::getStatus, 2).update();
                redisUtils.remove(WechatConstant.ORDER_PREFIX + order.getId());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(400, "微信支付服务错误，请联系管理员");
        }
    }

    @Override
    public OrderListVo listOrder(PageQuery pageQuery, Integer status, Integer isStatistics, LocalDateTime startTime, LocalDateTime endTime,Integer isSuiXi) {
        OrderListVo orderListVo = new OrderListVo();
        if (Integer.valueOf(1).equals(isStatistics)) {
            List<Order> orders = baseMapper.getAllOrder(status, pageQuery.getSearch(), startTime, endTime,isSuiXi);
            orderListVo.setSellNumber(orders.size());
            int sum = 0;
            if (orders.size() != 0) {
                List<Long> orderIds = new ArrayList<>();
                for (Order order : orders) {
                    if (order.getStatus() == 4) {
                        orderIds.add(order.getId());
                    }
                    sum += order.getMoneyAmount();
                }
                if (orderIds.size() > 0) {
                    QueryWrapper<Refund> queryWrapper = new QueryWrapper<>();
                    queryWrapper.in("order_id", orderIds);
                    queryWrapper.eq("status", 3);
                    List<Refund> refundList = refundMapper.selectList(queryWrapper);
                    for (Refund refund : refundList) {
                        sum -= refund.getActualMoneyAmount();
                    }
                }
            }

            orderListVo.setSellMoney(sum);
        }
        PageHelper.startPage(pageQuery.getPage(), pageQuery.getSize());
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        if (status != null) {
            queryWrapper.eq("status", status);
        }
        if(isSuiXi != null){
            queryWrapper.eq("charity_type", isSuiXi);
        }
        if (StringUtils.isNotEmpty(pageQuery.getSearch())) {
            queryWrapper.like("order_number", "%" + pageQuery.getSearch() + "%");
        }
        if (startTime != null && endTime != null) {
            queryWrapper.between("create_time", startTime, endTime);
        }
        if ("ASC".equals(pageQuery.getOrder())) {
            queryWrapper.orderByAsc(pageQuery.getOrderKey().split(";"));
        }
        if ("DESC".equals(pageQuery.getOrder())) {
            queryWrapper.orderByDesc(pageQuery.getOrderKey().split(";"));
        }
        PageInfo<Order> orders = new PageInfo<>(baseMapper.selectList(queryWrapper));
        if (orders.getList().size() == 0) {
            return null;
        }
        Map<Long, User> hashUser = userMapper.selectBatchIds(orders.getList().stream().map(Order::getUserId).collect(Collectors.toList())).stream().collect(Collectors.toMap(User::getId, u -> u));
        /*for(User user : hashUser.values()){
            user.setAvatarFileId(AliyunUtils.getDownloadLink(user.getAvatarUrl(), new Date().getTime() + SecurityConstant.TIME_ONE_DAY));
        }*/
        List<OrderVo> orderVos = new ArrayList<>();
        for (Order order : orders.getList()) {
            orderVos.add(new OrderVo(hashUser.get(order.getUserId()), order));
        }
        orderListVo.setOrderVos(new PageInfoUtils<Order, OrderVo>().copy(orders, orderVos));
        return orderListVo;
    }


    public List<OrderDetail> getOrderDetail(Long orderId, Integer refundStatus) {
        QueryWrapper<OrderDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id", orderId);
        if (refundStatus != null) {
            queryWrapper.eq("refund_status", refundStatus);
        }
        return orderDetailMapper.selectList(queryWrapper);
    }

    @Override
    public PageInfo<Order> getUserOrder(PageQuery pageQuery, Integer status) {
        PageHelper.startPage(pageQuery.getPage(), pageQuery.getSize());
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", SecurityUtils.getUserIdNumber());
        if (status != null) {
            queryWrapper.eq("status", status);
        }
        queryWrapper.orderByDesc("create_time");
        return new PageInfo<>(baseMapper.selectList(queryWrapper));
    }

    @Override
    public Map<String,Object> getFrequentlyUsedTable() {
        Map<String,Object> result = new HashMap<>();
        List<Tablet> tabletList = new LambdaQueryChainWrapper<>(tabletMapper)
                .eq(Tablet::getIsSave, 1)
                .eq(Tablet::getUserId, SecurityUtils.getUserIdNumber())
                .list();
        result.put("tabletList",tabletList);
        tabletList.forEach(Tablet::getTabletNumber);
        List<Long> tabletId = tabletList.stream().map(Tablet::getId).collect(Collectors.toList());
        if (tabletId.size() > 0) {
            QueryWrapper<Name> tabletNameQuery = new QueryWrapper<>();
            tabletNameQuery.in("tablet_id", tabletId);
            result.put("tabletNameList",nameMapper.selectList(tabletNameQuery));
        }
        return result;
    }

    @Override
    public void changeTabletIsSave(Long tabletId, Integer isSave) {
        Tablet tablet = tabletMapper.selectById(tabletId);
        if(!SecurityUtils.getUserIdNumber().equals(tablet.getUserId())){
            throw new BaseException(4000,"请勿操作不属于自己的牌位");
        }
        if(tabletId == null || isSave == null){
            throw new BaseException(4000,"传递参数为空");
        }
        if(isSave != 1 && isSave != 2){
            throw new BaseException(4000,"传递参数有误");
        }
        try{
            new LambdaUpdateChainWrapper<>(tabletMapper).set(Tablet::getIsSave,isSave).eq(Tablet::getId,tabletId).update();
        }catch (Exception e){
            throw new BaseException(4000,"更新失败");
        }
    }

    @Transactional
    @Override
    public void updateTablet(Tablet tablet) {
        tabletMapper.updateById(tablet);
    }

    @Override
    public void setPrintStatus(TabletIdListVo tabletIdListVo,Integer status) {
        status = status != 1 && status != 2 ? 2 : status;
        if(tabletIdListVo.getTabletIdList() != null && tabletIdListVo.getTabletIdList().size() > 0){
            new LambdaUpdateChainWrapper<>(tabletMapper)
                    .set(Tablet::getPrintStatus,status)
                    .in(Tablet::getId,tabletIdListVo.getTabletIdList())
                    .update();
        }
    }


    @Override
    public List<OrderDetailVo> getOrderDetail(Long orderId) {
        List<OrderDetail> orderDetailList = getOrderDetail(orderId, null);
        List<Long> tableId = new ArrayList<>();
        List<Long> charityId = new ArrayList<>();
        List<Long> stockId = new ArrayList<>();
        orderDetailList.forEach(odl -> {
            if (odl.getCharityId() != null) {
                charityId.add(odl.getCharityId());
            }
            if (odl.getTabletId() != null) {
                tableId.add(odl.getTabletId());
            }
            stockId.add(odl.getStockId());
        });
        Map<Long, List<Name>> tabletName = new HashMap<>();
        Map<Long, List<Name>> charityName = new HashMap<>();
        Map<Long, Tablet> tabletMap = null;
        Map<Long, Charity> charityMap = null;
        if (tableId.size() > 0) {
            QueryWrapper<Name> tabletNameQuery = new QueryWrapper<>();
            tabletNameQuery.in("tablet_id", tableId);
            tabletName = nameMapper.selectList(tabletNameQuery).stream().collect(Collectors.groupingBy(Name::getTabletId));
            List<Tablet> tablets = tabletMapper.selectBatchIds(tableId);
            tablets.forEach(Tablet::getTabletNumber);
            tabletMap = tablets.stream().collect(Collectors.toMap(Tablet::getId, t -> t));
        }
        if (charityId.size() > 0) {
            QueryWrapper<Name> charityNameQuery = new QueryWrapper<>();
            charityNameQuery.in("charity_id", charityId);
            charityName = nameMapper.selectList(charityNameQuery).stream().collect(Collectors.groupingBy(Name::getCharityId));
            charityMap = charityMapper.selectBatchIds(charityId).stream().collect(Collectors.toMap(Charity::getId, c -> c));
        }

        Map<Long, Stock> stockMap = stockMapper.selectBatchIds(stockId).stream().collect(Collectors.toMap(Stock::getId, s -> s));
        QueryWrapper<RefundDetail> refundDetailQueryWrapper = new QueryWrapper<>();
        refundDetailQueryWrapper.in("order_detail_id", orderDetailList.stream().map(OrderDetail::getId).collect(Collectors.toList()));
        refundDetailQueryWrapper.eq("status", 2);
        Map<Long, List<RefundDetail>> hashRefundDetail = refundDetailMapper.selectList(refundDetailQueryWrapper).stream().collect(Collectors.groupingBy(RefundDetail::getOrderDetailId));
        List<OrderDetailVo> result = new ArrayList<>();
        for (OrderDetail odl : orderDetailList) {
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            if (tabletMap != null && odl.getTabletId() != null) {
                orderDetailVo.setTablet(tabletMap.get(odl.getTabletId()));
                orderDetailVo.setNames(tabletName.get(odl.getTabletId()));
            }
            if (charityMap != null && odl.getCharityId() != null) {
                orderDetailVo.setCharity(charityMap.get(odl.getCharityId()));
                orderDetailVo.setNames(charityName.get(odl.getCharityId()));
            }
            Stock stock = stockMap.get(odl.getStockId());
            orderDetailVo.setStockRemark(stock.getRemark());
            orderDetailVo.setMoneyAmount(odl.getMoneyAmount());
            orderDetailVo.setOrderDetailId(odl.getId());
            orderDetailVo.setStatus(odl.getRefundStatus());
            orderDetailVo.setRefundAmount(hashRefundDetail.getOrDefault(odl.getId(), new ArrayList<>()).stream().mapToInt(RefundDetail::getActualMoneyAmount).sum());
            result.add(orderDetailVo);
        }
        return result;
    }

    @Override
    @Transactional
    public Map<String, Object> addStockOrder(OrderCreateVo orderCreateVo) {
        // 库存扣减
        processStock(orderCreateVo);
        // 调取基元数据
        List<Long> stockIds = new ArrayList<>();
        orderCreateVo.getOrders().forEach(t -> stockIds.add(t.getStockId()));
        List<Stock> stockList = stockMapper.selectBatchIds(stockIds);
        List<Long> activityIds = new ArrayList<>();
        stockList.forEach(t -> activityIds.add(t.getActivityId()));
        List<Activity> activityList = activityMapper.selectBatchIds(activityIds);
        Map<Long, Stock> stockMap = stockList.stream().collect(Collectors.toMap(Stock::getId, stock -> stock));
        Map<Long, Activity> activityMap = activityList.stream().collect(Collectors.toMap(Activity::getId, activity -> activity));
        // 支付价格核算
        List<OrderDetail> orderDetails = checkMoneyAmount(orderCreateVo, stockMap);
        // order创建
        Order order = createOrder(orderDetails, stockMap);
        //设置订单类型
        order.setCharityType(orderCreateVo.getCharityType());
        order.setStatus(2);
        // 微信支付PrePay
//        Map<String, String> wechatResult = wechatService.prepayOrder(order);
        // 自动填充及保存
        baseMapper.insert(order);
//        redisUtils.put(WechatConstant.ORDER_PREFIX + order.getId(),wechatResult.toString(),WechatConstant.THIRTY_MINUTES);
        orderDetails.forEach(t -> {
            t.setOrderId(order.getId());
            t.setRefundStatus(1);
        });
        checkAndFillData(orderCreateVo, stockMap, activityMap, orderDetails);
        List<String> failOrder = new ArrayList<>();
        for (OrderCreateMap orderCreateMap : orderCreateVo.getOrders()) {
            if (!orderCreateMap.getFlag()) {
                failOrder.add(stockMap.get(orderCreateMap.getStockId()).getRemark());
            }
        }

        return Map.of("wechatPay", "wechatPay", "failOrder", failOrder);
    }

    @Override
    public String continuePay(Long orderId) {
        Long userId = SecurityUtils.getUserIdNumber();
        Order order = baseMapper.selectById(orderId);
        if (userId.equals(order.getUserId())) {
            if (LocalDateTime.now().isBefore(order.getCreateTime().plus(30, ChronoUnit.MINUTES))) {
                return redisUtils.get(WechatConstant.ORDER_PREFIX+orderId);
            } else {
                throw new BaseException(400, "您的订单支付已过期，请重新下单");
            }
        } else {
            throw new BaseException(400, "您查询的订单不是您所支付的订单！");
        }
    }

    @Override
    @Transactional
    public void cancelOrder(Long orderId) {
        Long userId = SecurityUtils.getUserIdNumber();
        Order order = baseMapper.selectById(orderId);
        if (userId.equals(order.getUserId())) {
            if (order.getStatus() != 1) {
                throw new BaseException(400, "您的订单已付款，无法取消！");
            }
            List<OrderDetail> orderDetails = orderDetailMapper.selectByMap(Map.of("order_id", order.getId()));
            Map<Long, Stock> collect = stockMapper.selectBatchIds(orderDetails.stream().map(OrderDetail::getStockId).collect(Collectors.toSet())).stream().collect(Collectors.toMap(Stock::getId, s -> s));
            for (OrderDetail orderDetail : orderDetails) {
                Stock stock = collect.get(orderDetail.getStockId());
                if (stock.getAmount() != -1) {
                    for (int i = 0; i < 10; i++) {
                        Stock newStock = new Stock();
                        newStock.setId(stock.getId());
                        newStock.setSell(stock.getSell() + 1);
                        if (stockMapper.updateById(newStock) != 0) {
                            break;
                        }
                    }
                }
            }
            Order order1 = new Order();
            order1.setId(order.getId());
            order1.setStatus(4);
        } else {
            throw new BaseException(400, "您查询的订单不是您所支付的订单！");
        }
    }


    private List<OrderDetail> checkMoneyAmount(OrderCreateVo orderCreateVo, Map<Long, Stock> stockMap) {
        List<OrderDetail> orderDetails = new ArrayList<>();
        for (OrderCreateMap order : orderCreateVo.getOrders()) {
            Stock stock = stockMap.get(order.getStockId());
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setStockId(stock.getId());
            order.setOrderDetail(orderDetail);
            switch (stock.getMoneyType()) {
                case 1: {
                    orderDetail.setMoneyAmount(stock.getMoneyAmount());
                    break;
                }
                case 2: {
                    List<LimitMoney> limitMoney = stockMapper.getLimitMoney(stock.getId());
                    boolean flag = false;
                    for (LimitMoney money : limitMoney) {
                        if (money.getId().equals(order.getLimitMoneyId())) {
                            orderDetail.setMoneyAmount(money.getMoneyAmount());
                            flag = true;
                            break;
                        }
                    }
                    if (flag) {
                        break;
                    } else {
                        throw new BaseException(400, "您的捐赠金额不在限制范围内；若使用小程序外的方式提交订单，会导致封号！");
                    }
                }
                case 3:
                case 4: {
                    if (order.getMoneyAmount() <= 0) {
                        throw new BaseException(400, "捐赠金额不能少于1分钱人民币");
                    }
                    orderDetail.setMoneyAmount(order.getMoneyAmount());
                    break;
                }
            }
            orderDetails.add(orderDetail);
        }
        return orderDetails;
    }

    private Order createOrder(List<OrderDetail> orderDetails, Map<Long, Stock> stockMap) {
        Order order = new Order();
        int amountSum = 0;
        for (OrderDetail orderDetail : orderDetails) {
            amountSum += orderDetail.getMoneyAmount();
        }
        order.setMoneyAmount(amountSum);
        StringBuilder sb = new StringBuilder();
        sb.append(stockMap.get(orderDetails.get(0).getStockId()).getRemark());
        if (orderDetails.size() != 1) {
            sb.append("等").append(orderDetails.size()).append("个事项订单");
        }
        order.setRemark(sb.toString());
        Long userId = SecurityUtils.getUserIdNumber();
        User user = userMapper.selectById(userId);
        order.setOrderNumber(TimeUtils.getNumberTime() + StringUtils.beDigits(userId) + UUID.randomUUID().toString().split("-")[0].toUpperCase());
        order.setUserId(userId);
        order.setOpenId(user.getOpenId());
        order.setCreateTime(LocalDateTime.now());
        order.setStatus(1);
        return order;
    }

    private void processStock(OrderCreateVo orderCreateVo) {
        List<OrderCreateMap> orders = orderCreateVo.getOrders();
        for (OrderCreateMap order : orders) {
            boolean flag = false;
            // 乐观锁5次重试机会
            for (int i = 0; i < 5; i++) {
                Stock stock = stockMapper.selectById(order.getStockId());
                if(stock.getAmount() == -1){
                    flag = true;
                    break;
                } else if (stock.getAmount() - stock.getSell() > 0) {
                    Stock newStock = new Stock();
                    newStock.setId(stock.getId());
                    newStock.setSell(stock.getSell() + 1);
                    if (stockMapper.updateById(newStock) != 0) {
                        flag = true;
                        break;
                    }
                }
            }
            order.setFlag(flag);
        }
    }

    private void checkAndFillData(OrderCreateVo orderCreateVo, Map<Long, Stock> stockMap, Map<Long, Activity> activityMap, List<OrderDetail> orderDetails) {
        for (OrderCreateMap orderCreate : orderCreateVo.getOrders()) {
            if (!orderCreate.getFlag()) {
                continue;
            }
            Stock stock = stockMap.get(orderCreate.getStockId());
            Activity activity = activityMap.get(stock.getActivityId());
            if (activity.getTabletType() == null) {
                // 为慈善类型
                Charity charity = orderCreate.getCharity();
                if (activity.getCharityType() == 1) {
                    if (charity == null || StringUtils.isEmpty(charity.getPaperAddress())) {
                        throw new BaseException(400, "数据填写错误，若已支付请联系管理员");
                    }
                } else {
                    charity.setPaperAddress(null);
                }
                charityMapper.insert(charity);
                orderCreate.getNames().forEach(name -> name.setCharityId(charity.getId()));
                orderCreate.getOrderDetail().setCharityId(charity.getId());
            } else {
                // 为牌位类型
                Tablet tablet = orderCreate.getTablet();
                if (tablet == null) {
                    throw new BaseException(400, "数据填写错误，若已支付请联系管理员");
                }
                tablet.setTabletType(activity.getTabletType());
                tablet.setLastTabletType(activity.getLastType());
                tablet.setSizeType(stock.getTabletSize());
                tablet.setUserId(SecurityUtils.getUserIdNumber());
                tabletMapper.insert(tablet);
                orderCreate.getNames().forEach(name -> name.setTabletId(tablet.getId()));
                orderCreate.getOrderDetail().setTabletId(tablet.getId());
            }
            orderCreate.getNames().forEach(t -> nameMapper.insert(t));
        }
        for (OrderDetail orderDetail : orderDetails) {
            orderDetailMapper.insert(orderDetail);
        }
    }


}
